home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 10
/
AACD 10.iso
/
AACD
/
Magazine
/
Online
/
httpproxy
/
src
/
net_amitcp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-08-20
|
17KB
|
620 lines
/*(( "Header" */
/*
* $Id: net_amitcp.c,v 1.6 1996/08/11 22:25:15 mshopf Exp mshopf $
* (c) 1995-96 Matthias Hopf
*
* AmiTCP net protocol stack handler.
*/
/*
* $Log: net_amitcp.c,v $
* Revision 1.6 1996/08/11 22:25:15 mshopf
* reworked debug messages.
*
* Revision 1.5 1996/07/17 16:42:42 mshopf
* added local hostname fetch.
*
* Revision 1.4 1996/06/03 04:08:19 mshopf
* added error messages.
* added hostname caching.
* added timeouts.
*
* Revision 1.3 1996/04/26 05:14:03 mshopf
* added blocking mode and rudimentary strerror.
* V0.13 alpha 5 fix.
*
* Revision 1.2 1996/04/24 17:41:16 mshopf
* some fixes.
*
* Revision 1.1 1996/04/24 03:20:13 mshopf
* Initial revision
*
*/
/*)) */
/*(( "Includes" */
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/syslog.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <proto/exec.h>
#include <exec/types.h>
#include "net.h"
#include "debug.h"
#ifdef _AMIGA
# include <bsdsocket.h>
# define ioctl IoctlSocket
# define strcasecmp stricmp
typedef long ioctl_t;
typedef long sock_t;
typedef long len_t;
#else /* rudimentary unix support (untested in current version) */
# define CloseSocket close
typedef int ioctl_t;
typedef int sock_t;
typedef int len_t;
#endif
/*)) */
/*(( "Global variables" */
#define MAX_HCACHESIZE 32
struct Library *SocketBase = NULL;
static fd_set ReadSet;
static fd_set WriteSet;
static sock_t ServerSocket = -1;
static int BlockingMode;
static char *ErrorTxt = NULL; /* extended error information */
/* Struct for Hostname caching */
struct HostCache { /* no LRU so far... */
char Name [MAXHOSTNAMELEN];
struct in_addr Addr;
};
static struct HostCache HCache [MAX_HCACHESIZE]; /* initialised with 0s */
static struct HostCache *HCacheNext = HCache; /* next cache to be filled */
/* This array consists only user informative errors */
static char *ErrNoList[] = {
"/", /* 0 */
"Operation not permitted", /* EPERM */
NULL, /* ENOENT */
NULL, /* ESRCH */
"Interrupted system call", /* EINTR */
"Input/output error", /* EIO */
"Device not configured", /* ENXIO */
NULL, /* E2BIG */
NULL, /* ENOEXEC */
NULL, /* EBADF */
NULL, /* ECHILD */ /* 10 */
"Resource deadlock avoided", /* EDEADLK */
"Cannot allocate memory", /* ENOMEM */
"Permission denied", /* EACCES */
"Bad address", /* EFAULT */
NULL, /* ENOTBLK */
"Device busy", /* EBUSY */
NULL, /* EEXIST */
NULL, /* EXDEV */
"Operation not supported by device", /* ENODEV */
NULL, /* ENOTDIR */ /* 20 */
NULL, /* EISDIR */
"Invalid argument", /* EINVAL */
"Too many open files in system", /* ENFILE */
"Too many open files", /* EMFILE */
"Inappropriate ioctl for device", /* ENOTTY */
NULL, /* ETXTBSY */
NULL, /* EFBIG */
NULL, /* ENOSPC */
NULL, /* ESPIPE */
NULL, /* EROFS */ /* 30 */
NULL, /* EMLINK */
"Broken pipe", /* EPIPE */
NULL, /* EDOM */
NULL, /* ERANGE */
"Operation would blck", /* EAGAIN */
NULL, /* EINPROGRESS */ /* <-- should be caught by handler */
"Operation already in progress", /* EALREADY */
NULL, /* ENOTSOCK */
NULL, /* EDESTADDRREQ */
"Message too long", /* EMSGSIZE */ /* 40 */
NULL, /* EPROTOTYPE */
NULL, /* EPROTOOPT */
NULL, /* EPROTONOSUPPORT */
NULL, /* ESOCKTNOSUPPORT */
NULL, /* EOPNOTSUPP */
NULL, /* EPFNOSUPPORT */
NULL, /* EAFNOSUPPORT */
"Address already in use", /* EADDRINUSE */
"Can't assign requested address", /* EADDRNOTAVAIL */
"Network is down", /* ENETDOWN */ /* 50 */
"Network is unreachable", /* ENETUNREACH */
"Network dropped connection on reset", /* ENETRESET */
"Software caused connection abort", /* ECONNABORTED */
"Connection reset by peer", /* ECONNRESET */
"No buffer space available", /* ENOBUFS */
NULL, /* EISCONN */
NULL, /* ENOTCONN */
NULL, /* ESHUTDOWN */
NULL, /* ETOOMANYREFS */
"Connection timed out", /* ETIMEDOUT */ /* 60 */
"Connection refused", /* ECONNREFUSED */
NULL, /* ELOOP */
NULL, /* ENAMETOOLONG */
"Host is down", /* EHOSTDOWN */
"No route to host", /* EHOSTUNREACH */
NULL, /* ENOTEMPTY */
NULL, /* EPROCLIM */
NULL, /* EUSERS */
NULL, /* EDQUOT */
NULL, /* ESTALE */ /* 70 */
NULL, /* EREMOTE */
NULL, /* EBADRPC */
NULL, /* ERPCMISMATCH */
NULL, /* EPROGUNAVAIL */
NULL, /* EPROGMISMATCH */
NULL, /* EPROGUNAVAIL */
NULL, /* ENOLCK */
NULL, /* ENOSYS */
NULL /* EFTYPE */ /* 79 */
};
/*)) */
static const char *amitcp_strerror (int ErrNo);
/*(( "addcache ()" */
static void addcache (const char *Name, struct in_addr Addr)
{
if (strlen (Name) >= MAXHOSTNAMELEN) /* just to be sure */
return;
debug (D_NET, ("adding cache for '%s'\n", Name));
HCacheNext->Addr = Addr;
strcpy (HCacheNext->Name, Name);
if ( ++HCacheNext >= &HCache [MAX_HCACHESIZE] )
HCacheNext = HCache;
}
/*)) */
/*(( "getaddr ()" */
/* get host address by name, use cache */
static struct in_addr getaddr (const char *Name)
{
struct hostent *HostEnt;
struct in_addr r;
struct HostCache *c;
for (c = HCache; c < &HCache [MAX_HCACHESIZE]; c++)
if (strcmp (c->Name, Name) == 0)
return (c->Addr);
debug (D_NET, ("host '%s' not yet in cache\n", Name));
if ( (r.s_addr = inet_addr (Name)) +0 == -1)
{
if (! (HostEnt = gethostbyname (Name)) )
{
ErrorTxt = "Hostname lookup failed";
r.s_addr = (unsigned long) ~0;
return (r);
}
memmove (&r, HostEnt->h_addr, sizeof (r));
addcache (Name, *((struct in_addr *) HostEnt->h_addr));
}
return (r);
}
/*)) */
/*(( "getname ()" */
/* get host name by address, use cache */
/* returns pointer to static buffer */
static const char *getname (struct in_addr Addr)
{
struct hostent *HostEnt;
struct HostCache *c;
for (c = HCache; c < &HCache [MAX_HCACHESIZE]; c++)
if (c->Name [0] && memcmp (&c->Addr, &Addr, sizeof (Addr)) == 0)
return (c->Name);
debug (D_NET, ("hostadr not yet in cache\n"));
if (! (HostEnt = gethostbyaddr ((caddr_t) &Addr, sizeof (struct in_addr), AF_INET)) )
return (inet_ntoa (Addr));
addcache (HostEnt->h_name, Addr);
return (HostEnt->h_name);
}
/*)) */
/*(( "init ()" */
/* Check for bsdlibrary and initialice when everything's right */
/* -> Blockmode = FALSE in nonblocking mode */
/* <- TRUE on success, FALSE otherwise */
static int amitcp_init (int BlockMode)
{
struct servent *ServEnt;
struct hostent *HostEnt;
static char LocalHostName [MAXHOSTNAMELEN];
char *HostName;
debug (D_NET, ("AmiTCP/IP networking module check\n"));
if (SocketBase)
return TRUE;
if (! (SocketBase = OpenLibrary ("bsdsocket.library", 2L)))
return FALSE;
debug (D_NET, ("AmiTCP/IP networking ok\n"));
SetErrnoPtr (&errno, sizeof (errno));
BlockingMode = BlockMode;
/* get default 'http' protocoll port */
if (! (ServEnt = getservbyname ("http", "tcp")) )
syslog (LOG_WARNING, "unknown protocol 'http', using default port %d", DEFAULT_HTTPPORT);
else
{
NetAmiTCP.StdHttpPort = ServEnt->s_port;
if (NetAmiTCP.StdHttpPort != DEFAULT_HTTPPORT)
syslog (LOG_WARNING, "protocol 'http' doesn't use standard port %d", DEFAULT_HTTPPORT);
}
/* get local hostname */
if (! (HostName = getenv ("HOSTNAME")))
syslog (LOG_ERR, "cannot get local hostname (HOSTNAME environment variable is not set)");
else
{
NetAmiTCP.HostName = HostName;
if ( (HostEnt = gethostbyname (HostName)) )
{
strncpy (LocalHostName, HostEnt->h_name, MAXHOSTNAMELEN-1);
LocalHostName [MAXHOSTNAMELEN-1] = '\0';
NetAmiTCP.HostName = LocalHostName;
free (HostName);
}
else
syslog (LOG_WARNING, "cannot resolve local hostname");
}
/*
if (gethostname (LocalHostName, MAXHOSTNAMELEN) < 0)
syslog (LOG_ERROR, "cannot get local hostname");
else
{
NetAmiTCP.HostName = LocalHostName;
if (! (HostEnt = gethostbyname (LocalHostName)))
*/
return TRUE;
}
/*)) */
/*(( "exit ()" */
/* exit amitcp protocol stack handler */
static void amitcp_exit (void)
{
if (! SocketBase)
return;
if (ServerSocket >= 0)
CloseSocket (ServerSocket);
ServerSocket = -1;
CloseLibrary (SocketBase);
SocketBase = NULL;
}
/*)) */
/*(( "select ()" */
/* wait for incoming / outgoing data */
/* -> onNew: called on accepted socket, arguemnts: new fd, calling host's name (static) */
/* -> onTOut: called at least every MIN_TIMEOUT seconds, may be more often */
/* <- TRUE: received Ctrl-C - FALSE: ok or error */
static int amitcp_select (void (*onNew)(int, const char *), void (*onTOut)(void))
{
struct sockaddr_in PeerIn;
len_t PeerLen = sizeof (PeerIn);
sock_t Peer;
ioctl_t on = 1;
struct timeval OutTime = { MIN_TIMEOUT, 0 };
switch (select (FD_SETSIZE, &ReadSet, &WriteSet, NULL, &OutTime))
{
case 0:
onTOut ();
return FALSE;
case -1:
if (errno == EINTR)
return TRUE;
syslog (LOG_ERR, "select failed: %s", amitcp_strerror (errno));
return TRUE; /* and exit... */
}
/* Check server socket for new connections */
if (FD_ISSET (ServerSocket, &ReadSet))
{
if ( (Peer = accept (ServerSocket, (struct sockaddr *) &PeerIn, &PeerLen)) )
{
#ifdef FIOASYNC
if (ioctl (Peer, FIOASYNC, (caddr_t) &on) < 0)
{
syslog (LOG_ERR, "ioctl() failed for FIOASYNC: %s", strerror (errno));
CloseSocket (Peer);
return FALSE;
}
#endif
#ifdef FIONBIO
if (ioctl (Peer, FIONBIO, (caddr_t) &on) < 0)
{
syslog (LOG_ERR, "ioctl() failed for FIONBIO: %s", strerror (errno));
CloseSocket (Peer);
return FALSE;
}
#endif
/* look up hostname here */
onNew ((int) Peer, getname (PeerIn.sin_addr));
}
else
syslog (LOG_ERR, "accept() failed: %s", strerror (errno));
}
return FALSE;
}
/*)) */
/*(( "checkread/write ()" */
static int amitcp_checkread (int Fd)
{
return (FD_ISSET (Fd, & ReadSet));
}
static int amitcp_checkwrite (int Fd)
{
return (FD_ISSET (Fd, & WriteSet));
}
/*)) */
/*(( "read/write ()" */
static int amitcp_read (int Fd, char *Buffer, int Size)
{
int i;
if ( (i = recv ((sock_t) Fd, Buffer, (len_t) Size, 0)) < 0)
ErrorTxt = "Read failed";
return i;
}
static int amitcp_write (int Fd, char *Buffer, int Size)
{
int i;
if ( (i = send ((sock_t) Fd, Buffer, (len_t) Size, 0)) < 0)
ErrorTxt = "Write failed";
return i;
}
/*)) */
/*(( "server ()" */
/* open and set up server socket */
static int amitcp_server (int Port)
{
struct sockaddr_in SockIn;
ioctl_t on = 1;
if (ServerSocket)
CloseSocket (ServerSocket);
if ( (ServerSocket = socket (AF_INET, SOCK_STREAM, 0)) < 0)
{
ErrorTxt = "socket() for serverport failed";
return FALSE;
}
memset ((char *) &SockIn, 0, sizeof (struct sockaddr_in));
SockIn.sin_family = AF_INET;
SockIn.sin_addr.s_addr = INADDR_ANY;
SockIn.sin_port = htons (Port);
if (bind (ServerSocket, (struct sockaddr *) &SockIn, sizeof (struct sockaddr_in)) < 0)
{
ErrorTxt = "bind() failed for serverport";
return FALSE;
}
if (! BlockingMode)
{
#ifdef FIOASYNC
if (ioctl (ServerSocket, FIOASYNC, (caddr_t) &on) < 0)
{
ErrorTxt = "ioctl() failed for FIOASYNC";
return FALSE;
}
#endif
#ifdef FIONBIO
if (ioctl (ServerSocket, FIONBIO, (caddr_t) &on) < 0)
{
ErrorTxt = "ioctl() failed for FIONBIO";
return FALSE;
}
#endif
}
if (listen (ServerSocket, 5) < 0)
{
ErrorTxt = "listen() failed";
return FALSE;
}
return TRUE;
}
/*)) */
/*(( "open ()" */
/* open and set up server socket */
/* <- -1: error Fd: otherwise */
static int amitcp_open (const char *Host, int Port)
{
struct sockaddr_in SockIn;
ioctl_t on = 1;
sock_t Sock;
memset ((char *) &SockIn, 0, sizeof (struct sockaddr_in));
SockIn.sin_family = AF_INET;
SockIn.sin_port = htons (Port);
if ( (SockIn.sin_addr = getaddr (Host)).s_addr == ~0)
return -1;
if ( (Sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
{
ErrorTxt = "socket() failed";
return -1;
}
if (! BlockingMode)
{
#ifdef FIOASYNC
if (ioctl (Sock, FIOASYNC, (caddr_t) &on) < 0)
{
ErrorTxt = "ioctl() failed for FIOASYNC";
CloseSocket (Sock);
return -1;
}
#endif
#ifdef FIONBIO
if (ioctl (Sock, FIONBIO, (caddr_t) &on) < 0)
{
ErrorTxt = "ioctl() failed for FIONBIO";
CloseSocket (Sock);
return -1;
}
#endif
}
if (connect (Sock, (struct sockaddr *) &SockIn, sizeof (struct sockaddr_in)) < 0)
if (errno != EINPROGRESS)
{
ErrorTxt = "connect() failed";
CloseSocket (Sock);
return -1;
}
errno = 0;
return Sock;
}
/*)) */
/*(( "close ()" */
/* open and set up server socket */
static void amitcp_close (int Fd)
{
CloseSocket ((sock_t) Fd);
}
/*)) */
/*(( "initfd/setfdread/write ()" */
/* <- Server: accept incoming requests */
static void amitcp_initfd (int Server)
{
FD_ZERO (& ReadSet);
FD_ZERO (& WriteSet);
if (Server)
FD_SET (ServerSocket, & ReadSet);
}
static void amitcp_setfdread (int Fd)
{
FD_SET ((sock_t) Fd, &ReadSet);
}
static void amitcp_setfdwrite (int Fd)
{
FD_SET ((sock_t) Fd, &WriteSet);
}
/*)) */
/*(( "strerror ()" */
/* return enhanced error string */
static const char *amitcp_strerror (int ErrNo)
{
static char Err [128];
char *c;
if (ErrorTxt)
{
sprintf (Err, "%s: ", ErrorTxt);
c = Err + strlen (Err);
sprintf (c, (ErrNo < sizeof(ErrNoList) / sizeof(ErrNoList[0]) && ErrNo >= 0 && ErrNoList [ErrNo]) ? ErrNoList [ErrNo] : "Error #%d", ErrNo);
ErrorTxt = NULL;
}
else
sprintf (Err, (ErrNo < sizeof(ErrNoList) / sizeof(ErrNoList[0]) && ErrNo >= 0 && ErrNoList [ErrNo]) ? ErrNoList [ErrNo] : "Error #%d", ErrNo);
return (Err);
}
/*)) */
/*(( "Method dispatch table" */
netmethods_t NetAmiTCP = {
"AmiTCP V2.2",
"unknown",
FD_SETSIZE,
DEFAULT_HTTPPORT,
amitcp_init,
amitcp_exit,
amitcp_select,
amitcp_checkread,
amitcp_checkwrite,
amitcp_read,
amitcp_write,
amitcp_server,
amitcp_open,
amitcp_close,
amitcp_initfd,
amitcp_setfdread,
amitcp_setfdwrite,
amitcp_strerror
};
/*)) */